package com.tedu.api.io;

import org.junit.Test;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * File文件、文件夹：无法读写数据
 * RandomAccessFile：读写按照字节，读写类不明确，靠构造器传入第二个参数确定
 *
 * Java 提出一套 标准IO--读写单独提供类
 * ①方向区别:
 * 输入流---(读:针对文件，文件往程序中走，文件“相对于程序")
 * 输出流---(写:针对文件，文件从程序写出，文件“相对于程序")
 * 输入流和输出流创建方式?
 * 输入流     InputStream(抽象类)---顶级父类---读操作
 * 输出流	0utPutStream(抽象类)---顶级父类---写操作
 * 以上两个流定义所有流具备读写操作，本身无法实例化
 * ②按照功能进行区别:
 * 低级流:专门搬运数据的流【叫做:节点流]
 * FileInputStream	文件输入流:读操作---InputStream(抽象类)
 * OutputStream 文件输出流:写操作---OutputStream(抽象类)
 * 功能:建立在程序与数据源之间的"管道"，所有数据传输通过低级流[节点流]
 * 高级流:不能独立存在的流【叫做处理流]
 * 处理低级流的数据，并且可以被其他高级流所处理!
 * 理解:处理搬运数据的流【高级流种类比较多]
 *
 * ------------流:明确功能，创建对象:直接使用低级流--高级流创建
 项目:聊天室   Version 1 IO流：针对传字符串
 */
public class IOAPI {
    public IOAPI() throws FileNotFoundException {
    }

    @Test
    public void testIO() throws IOException {
        //创建标准I0流，方向划分:输入流于输出流--功能
        // 需求:从项目中写出"fox.txt”低级流:FileOutputStream
        // 创建文件输出流构造器("相对路径+文件全称")
// 抛出异常FileNotFoundException:没有文件的异常!
// 原因:路径或者文件名写错
        FileOutputStream fos =null;
        fos=new FileOutputStream("fos.txt");
        /**
         * 输出一句话：我
         * write(字符串：字符数组) 返回值：void
         * 作用：写出一个文件内容：字节数组
         *
         */
        //String 字符串 方法：getBytes()可以将字符串转换成字符数组 char[]
        fos.write("我是祖国的花朵".getBytes());
        //标准IO流必须关闭
        fos.close();//释放流：避免占用内存
    }

    /**
     * FileOutputStream
     * new FileOutputStream(String path, boolean append)
     * 通过第二个参数，确定数据是否追加，true代表追加，反之
     * new FileOutputStream(File file, boolean append);
     */
    @Test
    public void testIOA() throws IOException {
        //当前API项目下添加fos.txt---增加功能：追加效果
        //相对路径：./fos.txt直接写文件|不写./都是当前目录下
        FileOutputStream fos =
                new FileOutputStream("fos.txt", true);
        String str = "是未来~";
        fos.write(str.getBytes());
        //关闭流
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
        if (fos!=null)
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }




    /**
     * 低级流
     * 文件输入流
     * FileInputStream
     * read(字节数组) 返回值：int
     * 作用：读取到一个字节
     */
    @Test
    public void testFileInputStream() throws IOException {
        FileInputStream fis =null;
        try {
        fis=new FileInputStream("fos.txt");
        //10KB 字节数组
        byte[] bytes = new byte[1024 * 10];
        //10KB 读 数据 【不想看到整数值】
        int len = fis.read();
        //String字符串类--提供构造方法：按照字节数组量，输出字符串
        //String构造器 new String(字节数组，开始读取文件，结束读取文件)
        //将【二进制】整数 转换为字符串
        String str = new String(bytes, 0, len);//循环
        //一次性将给定的字节数组【10KB】写入到文件中，文件从0开始写入，到-1结束，写入完毕
        System.out.println("打印读：" + str);

            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis!=null)
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    //需求：重构RandomAccessFile类--复制视频的方法
    @Test
    public void testWork() throws IOException {
        //1.创建输入流--读--被复制文件【源文件】
        FileInputStream fis = null;
        fis = new FileInputStream("复制文件.MP4");
        //2.创建输出流--写--复制品
        FileOutputStream fos = null;
        fos = new FileOutputStream("标准IO.MP4");
        //3.用于保存读取到的字节量
        int len = -1;//①保存读取到的数据值【可以被覆盖】②-1代表文件结尾

        //6.视频空间大，解决方法：10KB，字节数组：设定保存值10KB
        byte[] bytes = new byte[1024 * 10];
        //4.一直读取【10KB】文件，保存len中，如果结果不等于-1，一直读
        //(len=fis.read(bytes))!=-1
        long start = System.currentTimeMillis();//开始毫秒数
        //4.1将读取到的【10KB】文件写出
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }
        long end = System.currentTimeMillis();//结束毫秒数
        System.out.println("测试传输时间：" + (end - start + "毫秒"));
        //5.关闭流

        fis.close();

        fos.close();
    }

    /**
     * 高级流【处理流】：处理低级流以及其他高级流的一”种“流
     * 高级流不能独立存在，一定处理低级流
     * BufferedInputStream---高级流：快速读  缓冲输入流·
     * BufferedOutputStream--高级流：快速写  缓冲输出流
     * 以上两个高级流，内嵌一套按照实际传输数据大小，随机型改表的字节数组
     * 高级流语法：
     * BufferedInputStream bis=new BufferedOutputStream(低级流引用);
     * 需求：视频 使用IO流【低级流+高级流】--实现视频快速复制
     */
    @Test
    public void testCV() throws IOException {
            //创建低级流：输入流：高级流、输出流：高级流
            FileInputStream fis =
                    new FileInputStream("复制文件.MP4");
            BufferedInputStream bis = new BufferedInputStream(fis);
            FileOutputStream fos =
                    new FileOutputStream("缓冲文件.MP4");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            //2.定义整数变量保存每次读取的字节量
            int len = -1;
            //3.将高级流：缓冲输入流--读取的数据--保存给交len，结果不等于-1
            //(len=bis.read())!=-1
            long start = System.currentTimeMillis();
            //read() 读取【缓冲字符输入流：自动产生合理字节数组】--->读取
            while ((len = bis.read()) != -1) {
                bos.write(len);
            }
            long end = System.currentTimeMillis();
            System.out.println("缓冲流花费时间" + (end - start));
            //4.关闭流
fis.close();
            fos.close();
        }


    /**
     * 【高级流】
     * BufferedOutputStream缓冲输出流
     * 需求：写出一份文件：bos.txt,写出一段文本内容
     * 所有的流，全部都不是正确的写法：IO流异常最多，采取捕获异常并作出处理
     */
    @Test
    public void testBOS() throws IOException {
        //创建低级流--写出bos.txt
        FileOutputStream fos=new FileOutputStream("bos.txt");
        //创建高级流--写出 一段文本内容【缓冲区：字节数据】
        BufferedOutputStream bos =new BufferedOutputStream(fos);
        String str = "112233";
        //write(byte[] b) 作用：写出字节数组
        bos.write(str.getBytes());
        /**
         * flush()
         * 作用：可以强制将内部缓冲区数据一次性读写
         * 缺点：频繁使用flush降低读写
         */
        //bos.flush();
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
        if (bos!=null)
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        if (fos!=null)
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }

    /**
     *
     *
     * 考试题:final与finally的区别?
     * final关键字:修饰变量、方法、类-->变量成常量
     * 方法不能重写
     * 类不能被继承
     * finally关键字:异常处理机制中:捕获异常
     * finally在捕获异常中， 可以写，，也可以不写
     * 所有异常都不符和，一定会执行的语法块
     * 写finally关键字所有异常都不符和，一定会执行的语法块
     * 通常使用在IO流中:关闭流为主!关闭高级流-->间接关闭低级流
     */

        @Test
        public void testBOSIO() throws IOException {
//1.需要将流置空:置空--①覆盖，不用多次创建对象!②关闭流
            FileOutputStream fos = null;//低级流--搬运数据:写出
            BufferedOutputStream bos = null;//高级流--处理低级流:缓冲输出流
            //2.捕获异常操作:alt+ctrl+T
            //2.1 创建输出流
            fos = new FileOutputStream("BOS标准流写法.txt");
            bos = new BufferedOutputStream(fos);
            //3.高级流:缓冲输出流---功能:缓冲[字节数组]写出数据
            bos.write("标准流写法".getBytes());
            //4.缓冲区:数据量比较小---采用flush()
            try{
                bos.flush();
            } catch (FileNotFoundException e) {//文件不存在的异常:路径写错
            e.printStackTrace();//打印异常信息
            System.out.println("【小】文件不存在异常");
        } catch (IOException e) {//IO流最大的异常：小异常不处理
            e.printStackTrace();
            System.out.println("【中】IO流异常");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("【大】异常");
        } finally {
                //一定执行代码块：IO流关闭--高级流--关闭高级流--低级流关闭【捕获异常】
                //                        低级流--关闭低级流【捕获异常】
        //5.先判断，高级流--是否为空【有没有高级流对象】--有高级流对象【必须关闭】
                //                                 没有高级流对象【不必关闭】
            if (bos!=null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (fos!=null)
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
    }
}